package ipcards.rulesets;

import ipcards.CardSet;
import ipcards.Deck;
import ipcards.Card;
import ipcards.Game;
import ipcards.GameBase;
import ipcards.PlayerModel;
import ipcards.Room;
import ipcards.events.PlayerAction;
import ipcards.net.Client;
import ipcards.net.CardsServer;

import java.awt.Point;
import javax.swing.*;

import java.awt.event.*;
import java.util.Vector;

public class ChaseTheAceGame extends GameBase implements Game {
	
	//actions
	public static final char SIT = 0;
	public static final char SWAP = 1;
	public static final char CUT = 2;
	
	
	private ChaseTheAceRuleSet ruleset;
	private String CTADeckName;
	private Room room;
	private CardSet CTADeck;
	//private static boolean Hking;
	//private static boolean Sking;
	//private static boolean Cking;
	//private static boolean Dking;
	//private static int numplayersleft;
	private int[] scores;
	//private boolean handDone = false;
	private boolean[] playersOut;
	private boolean wagon = true;
	
	private ActionListener al;
	public JPanel buttonPanel;
	private JButton sit;
	private JButton swap;
	
	public static int numplayers;
	//private CardServer server1;
	
	public ChaseTheAceGame(PlayerModel player, Room r, Client source) {
		super(player);
		room = r;
		
		//createButtonPanel();
		//send to view 
		
		//server1 = systemtoplevel.getCardServer();
		ruleset = new ChaseTheAceRuleSet(room.controller.getCardServer(), this);
		CTADeckName = ruleset.getDeckName();						//method needed in ChaseTheAceRuleSet
		
		room.table.addDeck(new Deck(CTADeckName));
	
		CTADeck = room.table.cards;
		CTADeck.shuffle();
		
		if(room.players.size() != 1) {
			
			int sourceID = 0;
			
			for(Client c: CardsServer.clients){
				if(c == source){
					break;
				}
				sourceID++;
			}
			//System.out.println(sourceID);			
			//Hking=false;
			//Sking=false;
			//Cking=false;
			//Dking=false;
			//RESET LOCAL FLIP NOT WORKING HERE, AND NOT OCCURING FOR FIRST DEAL
			//			resetLocalFlipComplete();
			//System.out.println("should have reset");
			//
			//Resolved by calling in ServerModel on any occurance of a new game			
			//ruleset.setDealerID(sourceID);
			
			int ttt = room.controller.getCardServer().getTurn();	//ttt = current turn...equivilent to player who started game....should be first to play, hence previous player should be dealer
			if(ttt == 0){
				ttt = CardsServer.clients.size();
			}
			ttt--;		//id of previous player to person who started game. will become dealer, as person who started the game will play first
			ruleset.setDealerID(ttt);			
//System.out.println("turn "+ttt+" :: source "+sourceID+" :: dealer "+ruleset.getDealerID());			
			ruleset.deal((room.players.size() - 1), CTADeck);			//subtract 1 to compensate for server not being a client
						
			scores = new int[CardsServer.clients.size()];
			playersOut = new boolean[CardsServer.clients.size()];
			
			for(int clientnum = 0; clientnum < CardsServer.clients.size(); clientnum++) {			
				scores[clientnum] = ruleset.initialLives();
				playersOut[clientnum] = false;
			}											//use this (server end) to set static variable numplayersleft equal to number of players; decrement numplayersleft for each pass thru else (client); when numplayersleft is zero (indicating last player), call SYNC_LOCATIONS (see below)
			
			numplayers = room.players.size() - 1;			

		}
		/*else {
			int playernum = 0;
			//System.out.println("client");				//decrement static variable numplayersleft for each pass through
			numplayersleft--;
			for(Card c : CTADeck) {
				for(Player p : room.players){
					//System.out.println(playernum);
					//if(c.permissionQuery(room.players.get(playernum++), room.controller.getCardServer().SEE) && c.getAttribute("rank").equalsIgnoreCase("13")) {
					if(Hking || Sking || Cking || Dking) {
						//System.out.println("player 0 has see permissions on card "+c.getAttribute("rank") + " of " + c.getAttribute("suit") + "s.");
					}
					//System.out.println(playernum);
				}
				//if(c.permissionQuery(room.players.get(0), room.controller.getCardServer().SEE)) {
				if(c.getAttribute("rank").equalsIgnoreCase("13")) {	
					if(c.getAttribute("rank").equalsIgnoreCase("13")) {
						if(c.getAttribute("suit").equals("0") && Cking) {
					 		c.setFaceUp();
					 		//System.out.println("flipping king");
				 		}
				 		else if(c.getAttribute("suit").equals("1") && Sking) {
					 		c.setFaceUp();
					 		//System.out.println("flipping king");
				 		}
				 		else if(c.getAttribute("suit").equals("2") && Hking) {
					 		c.setFaceUp();
					 		//System.out.println("flipping king");
				 		}
				 		else if(c.getAttribute("suit").equals("3") && Dking) {
					 		c.setFaceUp();
					 		//System.out.println("flipping king");
				 		}
				 	}
				 	//System.out.println("entering flipping zone");
			 	}
			 	playernum = 0;
		 	}
		 	//System.out.println(numplayersleft);
		 	//PlayerAction pa = new PlayerAction(x, PlayerAction.SYNC_LOCATIONS);			//activate this ONLY on the last pass through, as specified by numplayersleft
		 	//x.clients.get(0).getServerModel().playerActionPerformed(pa);
	 	}*/
		
		//CardsServer s = systemtoplevel.getCardServer();
		//if(s == null){System.out.println("server is null");}
	}
	
	//Generic methods
	public JPanel createButtonPanel(){
		buttonPanel = new JPanel();
		al = new buttonPanelListener();
		sit = new JButton("Sit");
			sit.setMnemonic(KeyEvent.VK_S);
			sit.addActionListener(al);
		swap = new JButton("Swap");
			swap.setMnemonic(KeyEvent.VK_W);
			swap.addActionListener(al);
				
		buttonPanel.add(sit);
		buttonPanel.add(swap);
		return buttonPanel;
	}
	
	public class buttonPanelListener implements ActionListener{
		public void actionPerformed(ActionEvent ae) {
				
			Object source = ae.getSource();
			if(source == sit){
				player.performPlayerAction(PlayerAction.newCustomPlayerAction(this, SIT, null));
			}
			else if(source == swap){
				player.performPlayerAction(PlayerAction.newCustomPlayerAction(this, SWAP, null));				
			}
		}
	}
	
	public Room getRoom() {
		return room;
	}
	
	//Returns the start location (as a point) of a given player
	public Point getPlayerLocation(int playerNumber) {
		int value = 150*playerNumber;
		int xpos = (value%1050);
		int ypos = (value/1050)*150;
		//System.out.println(xpos+","+ypos);
		return new Point(15 + xpos,15+(ypos*150));
	}
	
	//Returns the orientation of the player as an angle from 0 to 359 as an integer, with 0 corresponding to positive horizontal
	public int getPlayerAngle(int playerNumber) {	//Not currently used in CTA
		return 0;
	}
	
	//Returns the central point of a players hand, about which all cards are situated
	public Point getPlayerLocationCircle(int playerNumber) {				 
		int numPlayers = room.players.size() - 1;
		double angle = 360/(numPlayers);
		System.out.println(angle);
		double radius = 400+(numPlayers-1)*3;					//WILL NEED ADJUSTMENT
		
		
		int x = (int)(radius * Math.cos(Math.toRadians(playerNumber*angle + 90)));
		int y = (int)(radius * Math.sin(Math.toRadians(playerNumber*angle + 90)));
		return new Point(x+465,y+465);
	}
	
	//Returns the orientation of all players as an array of integers (in the format [x, y]) representing the displacement from one card to the next,
	//with +x corresponding to left, and +y corresponding to down
	public int[] getPlayerDirectionFixed() {	//Not currently used in CTA
		int[] dir = new int[2];
		dir[0] = 150%1050;
		dir[1] = 0;
		return dir;	
	}
	
	//Game Specific Methods
	public void sit(Client current){
		System.out.println("Client is sitting");
		ruleset.sit(current);
	}
	
	/*public void isKing(String suit) {
		if(suit.equals("hearts")) {
			Hking = true;
		} else if(suit.equals("spades")) {
			Sking = true;
		} else if(suit.equals("clubs")) {
			Cking = true;
		} else if(suit.equals("diamonds")) {
			Dking = true;
		}
		System.out.println("updating variable for king of " + suit);
	}*/
	

	/**
	 * This is where custom actions come into the game
	 */
	public void playerActionPerformed(PlayerAction pa) {
		Client current = null;
		Client next = null;
		Client dealer = null;
		CardsServer cs = room.controller.getCardServer();
		int dealerID = ruleset.getDealerID();
		int currentID = 0;
		boolean iscurrent = false;
	
		if(room.players.size() != 1) {
			//System.out.println("FOR THE LOVE OF GOD AND EVERYTHING ELSE THAT IS HOLY.....IM AT THE SERVER!!!!!!!!!!");
				
			switch (pa.getCustomAction()) {
				
			case SIT:
				//System.out.println("SERVER RECIEVED SIT");
				Client current2 = ((Client)(pa.getSource()));
				ruleset.sit(current2);
				
				for(int q=0;q<playersOut.length;q++){if(playersOut[q]){System.out.println("player "+q+"is out!!!!!");}}
				Client next2 = null;
				int currentID2 = 0;
				boolean iscurrent2 = false;
				for(Client c: cs.clients){
					if(iscurrent2){
						next2 = c;
						if(playersOut[currentID2]){				//if player is out
							next2.endTurn();						//end their turn and try the next player on the list
							next2 = null;
							iscurrent2 = true;					//hypothetically still with current player
						}
						else{
							iscurrent2 = false;					//found next player who is not out...it is now their turn
							System.out.println("set next");
						}
					}
					if(c == current2){
						iscurrent2 = true;						//found current player....start searching for the next still in player on the list
						System.out.println("found current");
					}
					
					currentID2++;
				}
				
				if(next2 == null){								//reached the end of the list and still no next player?
					boolean isNull2 = true;						//repeat process starting from top of list
					currentID2 = 0;
					for(Client c: cs.clients){
						if(!playersOut[currentID2]){				//if player is not out
							next2 = c;							//set next player and exit
							isNull2 = false;
							break;
						}
						c.endTurn();							//otherwise, move on to the next player
						currentID2++;
					}
				}		
				current2.getServerModel().sync();
					
				/*Determine if dealer, and hence end of hand*/
				CardsServer cs3 = room.controller.getServer();
				int dealerID3 = ruleset.getDealerID();
				int currentID3 = 0;
				Client dealer3 = null;
				for(Client c: cs3.clients){
					if(currentID3 == dealerID3){
						dealer3 = c;
						System.out.println("set dealer"+dealerID3);
						break;
					}
					currentID3++;
				}
				
				if(current2 == dealer3){
					System.out.println("Hand is over. Determine losers.");
					determineLosers(cs.clients, dealer3, current2);
				}
				current2.getServerModel().sync();
			break;
				
			case SWAP:

				current = ((Client)(pa.getSource()));
			
				
				for(Client c: cs.clients){
					if(iscurrent){
						next = c;
						if(playersOut[currentID]){				//if player is out
							//next.endTurn();						//end their turn and try the next player on the list
							next = null;
							iscurrent = true;					//hypothetically still with current player
						}
						else{
							iscurrent = false;					//found next player who is not out...it is now their turn
							System.out.println("set next");
						}
					}
					if(c == current){							//found current player....start searching for the next still in player on the list
						iscurrent = true;
						System.out.println("found current");
					}
					if(currentID == dealerID){					//found dealer
						dealer = c;
						System.out.println("set dealer");
					}
					currentID++;
				}
				
				if(next == null){								//reached the end of the list and still no next player?
					boolean isNull = true;						//repeat process starting from top of list
					currentID = 0;
					for(Client c: cs.clients){
						if(!playersOut[currentID]){				//if player is not out
							next = c;							//set next player and exit
							isNull = false;
							break;
						}
						//c.endTurn();							//otherwise, move on to the next player
						currentID++;
					}
				}		
				
				ruleset.swapCards(current,next,dealer,CTADeck);
				
				
				currentID = 0;
				for(Client c: cs.clients){
					if(iscurrent){
						next = c;
						if(playersOut[currentID]){				//if player is out
							next.endTurn();						//end their turn and try the next player on the list
							next = null;
							iscurrent = true;					//hypothetically still with current player
						}
						else{
							iscurrent = false;					//found next player who is not out...it is now their turn
							System.out.println("set next");
						}
					}
					if(c == current){							//found current player....start searching for the next still in player on the list
						iscurrent = true;
						System.out.println("found current");
					}
					if(currentID == dealerID){					//found dealer
						dealer = c;
						System.out.println("set dealer");
					}
					currentID++;
				}
				
				if(next == null){								//reached the end of the list and still no next player?
					boolean isNull = true;						//repeat process starting from top of list
					currentID = 0;
					for(Client c: cs.clients){
						if(!playersOut[currentID]){				//if player is not out
							next = c;							//set next player and exit
							isNull = false;
							break;
						}
						c.endTurn();							//otherwise, move on to the next player
						currentID++;
					}
				}
				
				//update clients
				current.getServerModel().sync();														//MUST USE THIS LINE MORE OFTEN!!!!!!!!!!!!!!!!!
				
				
				
			break;
			
			//Player has sent a cut location to server
			case CUT:
				
////////////////////////////////////////
//this code is in at least 2 cases, tidy up!!!!!!!!
/////////////////////////////////////////
				currentID = 0;
				for(Client c: cs.clients){
					if(currentID == dealerID){
						dealer = c;
						break;
					}
					currentID++;
				}
				
				///////////////////////////////////////
				
//#########################################SHOULD PROBABLY BE SOME CHECKING HERE so that people cant just cut the deck whenever they want
				current = ((Client)(pa.getSource()));
				ruleset.cutDeck(current, CTADeck, Integer.parseInt(pa.getArgs().substring(1)));
				
				//update clients
				current.getServerModel().sync();
				
				//Determine if dealer, and hence end of hand
				if(current == dealer) {
					System.out.println("Hand is over. Determine losers.");
					determineLosers(cs.clients, dealer, current);
				}	
				break;
			}
			
		//This is a client - ONLY DO CLIENT SIDE STUFF HERE
		} else {
			
			switch (pa.getCustomAction()) {
			
			case CUT:
				//Ask client what position to cut at
				boolean cutsuccess = false;
				int cutLocation = -1;
				int numCardsInDeck = 0;
				for(Card c: CTADeck){
					if(c.getLocation().equals(new Point(465,465))){
						numCardsInDeck++;
					}
				}
				
				while(!cutsuccess) {

					try {
						//+1 to card numbers to make it more human-friendly
						cutLocation = Integer.parseInt((String)JOptionPane.showInputDialog(
	            	        null,
	            	        "You are the dealer. You must cut the deck. \n Please choose the point at which to cut the deck [1 - " + numCardsInDeck + "]",
	            	        "Choose a number corresponding to the point in the deck"));
					}					
					catch(NumberFormatException e) {}
					//###########################################################
					//##### CANNOT USE getRoom().players.size() TO ENSURE 	#####
					//##### THE CUT IS IN RANGE HERE AS THIS IS ON THE 		#####
					//##### CLIENT SIDE, AND HENCE getRoom().players.size()	#####
					//##### IS ALWAYS GOING TO BE ONE, REGARDLESS OF THE	#####
					//##### NUMBER OF PLAYERS IN THE GAME!!!!!!!!!!!!!		#####
					//###########################################################			
if(Integer.toString(cutLocation) == null){
	System.out.println("tis null");
}					
					if(cutLocation > 0 && cutLocation <= numCardsInDeck){
						cutsuccess = true;
					}
				}
				
				//Send cut location to server
				player.performPlayerAction(PlayerAction.newCustomPlayerAction(this, CUT, cutLocation + ""));
				
				break;
					
			}			
		}
	}
	
	public void determineLosers(Vector<Client> players, Client dealer, Client current){
		System.out.println("Ended hand");
		Vector<Client> losers = ruleset.determineLosers(players, playersOut);
		System.out.println("Losers found?" + losers.size());
		
		CardSet faceUp = new CardSet();
		for(Card c : room.table.cards) {
			if(c.isFaceUp()) {
				faceUp.add(c);
			}
		}
		if(!faceUp.isEmpty()) {
//System.out.println("aelihfvljakehfahdfjahedfoiahewdfiuahdrifuhaeidfuhaqeoifuhqoiwuhdfoiquewhdfiauwdhfiuabdefviuahewrfihberfiuahdoifuhaiwdoufhaidufhawioeufhwireauhfalidhfvbaoiduhvaiuehvaiouefiahfvpaieuhfdpiaerufhpieaurhfapieruhvpef9iuvbpeiqurnveieufhviuaergfpiauegrfpihaw");			
			room.table.sModel.performPlayerAction(new PlayerAction(room.table.sModel, PlayerAction.SET_FACE_UP, faceUp.toString()));
		}		
		
		int clientnum = 0;		
		for(Client c: players){
			if(losers.contains(c)){
				scores[clientnum]--;
			}
			clientnum++;
		}
		
		for(int i=0;i<scores.length;i++){ 		
			System.out.println("Player "+i+":"+scores[i]);	
		}
		//handDone = true;	
		
		//room.table.sModel.sync();

		try{
			Thread.sleep(3000);
		}
		catch(InterruptedException e){
		}
	
		/*REDEAL*/	
	
		//int numplayers = players.size();
		boolean setwagon = false;
		
		for(int i=0;i<scores.length;i++){
			if(scores[i] == 0 && !wagon){
				playersOut[i] = true;
			}
			else if(scores[i] == 0 && wagon){
				setwagon = true;
				System.out.println("Player " + i + " is on the wagon");
				scores[i] = 1;
			}
		}
		
		if(setwagon){
			wagon = false;
			for(int i=0;i<scores.length;i++){ 		
				System.out.println("Player "+i+":"+scores[i]);	
			}
		}
		
		int stillIn = players.size();
		for(int i=0;i<playersOut.length;i++){
			if(playersOut[i]){
				System.out.println("Player " + i + " is out");
				stillIn--;
			}
		}
		if(stillIn <= 1){
//Game over: clear cards from table and identify/display winner
				System.out.println("Game Over");
				ruleset.canNewGame = true;
			return;
		}
		
		int dealID = ruleset.getDealerID();
		dealID++;
		if(dealID >= players.size()){
			dealID = 0;
		}
		if(playersOut[dealID]){				//make sure the dealer is not someone who is out of the game
			while(playersOut[dealID]){
				dealID++;
				if(dealID >= players.size()){
					dealID = 0;
				}
			}
		}
		ruleset.setDealerID(dealID);
		System.out.println("Dealer: "+dealID);
		
		
		ruleset.deal(players.size(), CTADeck, playersOut);

		CardSet toFlip = new CardSet();
		for(Card c : room.table.cards) {
			if(c.isFaceUp()) {
				toFlip.add(c);
			}
		}
		if(!toFlip.isEmpty()) {
			room.table.sModel.performPlayerAction(new PlayerAction(room.table.sModel, PlayerAction.SET_FACE_UP, toFlip.toString()));
		}
		CardSet cards = room.table.cards;
		//TO EDIT TO EDIT TO EDIT TO EDIT TO EDIT TO EDIT TO EDIT TO EDIT TO EDIT TO EDIT TO EDIT TO EDIT TO EDIT TO EDIT TO EDIT 
		room.table.sModel.performPlayerAction(new PlayerAction(room.table.sModel, PlayerAction.RESET_LOCAL_FLIP, cards.toString()));
	
		boolean iscurrent = false;
		Client next = null;
		int currentID = 0;
		int nextID = -1;
		for(Client c: players){
			if(iscurrent){
				next = c;
				if(playersOut[currentID]){				//if player is out
					next.endTurn();						//end their turn and try the next player on the list
					next = null;
					iscurrent = true;					//hypothetically still with current player
				}
				else{
					iscurrent = false;					//found next player who is not out...it is now their turn
					nextID = currentID;
					System.out.println("set next");
				}
			}
//CURRENT NOT THE DEALER?----->MUST RESOLVE
			if(c == current){							//found current player....start searching for the next still in player on the list
				iscurrent = true;
				System.out.println("found current");
			}
			if(currentID == dealID){					//found dealer
				dealer = c;
				System.out.println("set dealer");
			}
			currentID++;
		}
		
		if(next == null){								//reached the end of the list and still no next player?
			boolean isNull = true;						//repeat process starting from top of list
			currentID = 0;
			for(Client c: players){
				if(!playersOut[currentID]){				//if player is not out
					next = c;							//set next player and exit
					nextID = currentID;
					isNull = false;
					break;
				}
				c.endTurn();							//otherwise, move on to the next player
				currentID++;
			}
		}
		dealer.endTurn();
		next.endTurn();		
		
		int newID = nextID+1;							//ensure nextID + 1 will not be greater than the size of the list of players (i.e., ensure the players list wraps around)
		if(newID >= players.size()){					//if not, reset the nextID so that nextID + 1 will be 0
			nextID = -1;
		}
		
		if(playersOut[nextID+1]){						//if new next player is not in the game
			newID = 0;
			for(Client c: players){
				if(newID == (nextID+1) && playersOut[nextID+1]){					//at next player and they are out
					c.endTurn();													//end their turn and try next player
					nextID++;
					if((nextID+1) >= players.size()){
						nextID = -1;
					}
				}
				else if(newID == (nextID+1) && !playersOut[nextID+1]){				//at next player and they are NOT out
					break;
				}
				newID++;
			}
		}
					
				
		room.table.sModel.sync();
	}
	
	public void resetLocalFlip(Card card){
		CardSet temp = new CardSet();
		temp.add(card);
		room.table.sModel.performPlayerAction(new PlayerAction(room.table.sModel, PlayerAction.RESET_LOCAL_FLIP, temp.toString()));
	}
	public void resetLocalFlipComplete(){
//System.out.println("in method");		
		CardSet temp = new CardSet();
		for(Card c: room.table.cards){
			temp.add(c);
//System.out.println(c.getAttribute("rank")+" of "+c.getAttribute("suit")+"added");
		}
		room.table.sModel.performPlayerAction(new PlayerAction(room.table.sModel, PlayerAction.RESET_LOCAL_FLIP_TOALL, temp.toString()));
	}
	
	public boolean legalityCheck(String option){
		if(option.equalsIgnoreCase("shuffle")){
			return ruleset.canShuffle;
		}
		if(option.equalsIgnoreCase("newgame")){
			return ruleset.canNewGame;
		}
		return false;
	}
}
